<script setup lang="ts">
import { h, onBeforeMount, provide, shallowRef, type VNode, watch } from "vue";
import type { RenderNavigationInfo, SimpleNavigationContext, SimpleNavigationInfo } from "./simple-navigation-helper";
import SimpleNavigationItem from "./simple-navigation-item.vue";
import type { ComponentType } from "@/framework/vue/types";
import { noop } from "@/framework/util/warning";
import Strings from "@/framework/util/strings";

const props = defineProps<{
  navigations: SimpleNavigationInfo[];
  id?: string;
  defaultPath?: string;
  defaultPathArgs?: any;
}>();
const emits = defineEmits<{
  (e: "navigation-changed", navigations: RenderNavigationInfo[]): void;
}>();

const renderNavigations = shallowRef<RenderNavigationInfo[]>([]);
watch(renderNavigations, value => emits("navigation-changed", value));

const getRenderNavigationInfo = async (path: string, args?: any): Promise<RenderNavigationInfo> => {
  const nav = props.navigations!.find(it => it.path === path);
  let vnode: VNode;
  if (!nav) {
    vnode = h("span", { style: { color: "red" } }, `Not found: ${path}`);
  } else {
    if (typeof nav.component === "function") {
      try {
        const resp = await (nav.component as Function)();
        vnode = h(resp.default as ComponentType, nav.props);
      } catch (e: any) {
        vnode = h("span", { style: { color: "red" } }, `Component error : ${e.message}`);
        console.error(e);
      }
    } else {
      vnode = h(nav.component);
    }
  }
  return {
    key: `${path}_${Strings.randomStr()}`,
    path,
    name: nav?.name,
    component: vnode,
    args,
  };
};

const ctx: SimpleNavigationContext = {
  push: async (path, args) => {
    const navInfo = await getRenderNavigationInfo(path, args);
    renderNavigations.value = [...renderNavigations.value, navInfo];
  },
  replace: async (path, args) => {
    const navInfos = renderNavigations.value.slice();
    const navInfo = await getRenderNavigationInfo(path, args);
    navInfos.splice(navInfos.length - 1, 1, navInfo);
    renderNavigations.value = navInfos;
  },
  pop: count => {
    count = Math.min(count, renderNavigations.value.length - 1);
    if (count > 0) {
      renderNavigations.value = renderNavigations.value.slice(0, renderNavigations.value.length - count);
    }
  },
  popToTop: () => {
    if (renderNavigations.value.length > 1) {
      renderNavigations.value = renderNavigations.value.slice(0, 1);
    }
  },
  replaceToTop: async (path: string, args?: any) => {
    if (renderNavigations.value.length === 0) {
      const navInfo = await getRenderNavigationInfo(path, args);
      renderNavigations.value = [navInfo];
    }
    const topNav = renderNavigations.value[0];
    if (topNav.path === path) {
      if (renderNavigations.value.length > 1) {
        topNav.args = args;
        renderNavigations.value = [topNav];
      }
      return;
    }

    const navInfo = await getRenderNavigationInfo(path, args);
    renderNavigations.value = [navInfo];
  },
  back: () => {
    if (renderNavigations.value.length > 1) {
      const list = renderNavigations.value.slice();
      list.pop();
      renderNavigations.value = list;
      return true;
    }
    return false;
  },
};

provide("simple-navigation-context", ctx);
if (props.id) {
  provide(`simple-navigation-context:${props.id}`, ctx);
}
defineExpose(ctx);

onBeforeMount(() => {
  if (renderNavigations.value.length === 0 && props.navigations && props.navigations.length > 0) {
    const defaultPath = props.defaultPath || props.navigations[0].path;
    ctx.push(defaultPath, props.defaultPathArgs).then(noop);
  }
});

const onMouseUp = (e: MouseEvent) => {
  if (e.button === 3) {
    // 鼠标侧边键快捷返回
    if (ctx.back()) {
      e.preventDefault();
    }
  }
};
</script>

<template>
  <div class="simple-navigation" @mouseup="onMouseUp">
    <template v-for="(it, i) in renderNavigations" :key="it.key">
      <simple-navigation-item :visible="i === renderNavigations.length - 1" :args="it.args">
        <component :is="it.component" v-show="i === renderNavigations.length - 1" />
      </simple-navigation-item>
    </template>
  </div>
</template>

<style lang="less">
.simple-navigation {
  height: 100%;
  width: 100%;
  overflow: auto;
}
</style>
